package pt.isel.pdm.ouryamba;

import java.io.ByteArrayInputStream;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.Menu;
import android.widget.ImageView;
import android.widget.TextView;

public class UserInfoActivity extends BaseActivity implements ServiceConnection
{
	private Messenger serviceMessenger, thisMessenger;
	private Intent serviceIntent;
	private TextView user_name, status_number, subscribers_number, subscriptions_number;
	private ImageView image;
	private Bundle data;
	private Drawable image_drawable;
	private final String ID = getClass().getSimpleName();
	private General app;
	
	
//ServiceConnection methods
	
	/**
	 * Creates a messenger binded with the service.
	 * 
	 * This callback is executed when the bind between this client and the service is completed.
	 * */
	@Override
	public void onServiceConnected(ComponentName className, IBinder service)
	{
		serviceMessenger = new Messenger(service);
		getUserInfo();
		getUserImage();
	}
	
	/**
	 * Clears the messenger meaning it's no longer binded to the service.
	 * 
	 * This callback is executed when the service and this client are not binded anymore.
	 * */
	@Override
	public void onServiceDisconnected(ComponentName className)
	{
		serviceMessenger = null;
	}
	
	
//Own methods
	
	/**
	 * Fetch the user information.
	 * 
	 * If this client is not binded with the service, nothing is fetched and this method returns
	 * immediately.
	 * If not, is sent a message to the service asking for the user information and in that message
	 * is specified the messenger that should be notified when the information is fetched. The
	 * messenger is the one stored in {@code thisMessenger} variable.
	 * 
	 * If any exception is thrown, it's catalog in the application log.
	 * 
	 * @see General#log(String)
	 * @see General#logException(Exception)
	 * */
	private void getUserInfo()
	{
		if(serviceMessenger == null)
		{
			return;
		}
		Message msg = Message.obtain(null, UserInfoPullService.GET_USER_INFO);
		msg.replyTo = thisMessenger;
		try
		{
			serviceMessenger.send(msg);
		}
		catch(RemoteException e)
		{
			app.log(ID + ".getUserInfo(): Exception thrown.");
			app.logException(e);
		}
	}
	
	/**
	 * Fetch the profile image.
	 * 
	 * If this client is not binded with the service, nothing is fetched and this method returns
	 * immediately.
	 * If not, is sent a message to the service asking for the profile image and in that message
	 * is specified the messenger that should be notified when the information is fetched. The
	 * messenger is the one stored in {@code thisMessenger} variable.
	 * 
	 * If any exception is thrown, it's catalog in the application log.
	 * 
	 * @see General#log(String)
	 * @see General#logException(Exception)
	 * */
	private void getUserImage()
	{
		if(serviceMessenger == null)
		{
			return;
		}
		Message msg = Message.obtain(null, UserInfoPullService.GET_USER_IMAGE);
		msg.replyTo = thisMessenger;
		try
		{
			serviceMessenger.send(msg);
		}
		catch(RemoteException e)
		{
			app.log(ID + ".getUserImage(): Exception thrown.");
			app.logException(e);
		}
	}

	/**
	 * Initializes the messenger that will receive the service message after the information is fetched.
	 * 
	 * Defines a handler that will process the message from the service and assign that handler to
	 * the this messenger.
	 * The handler will copy the bundle (to {@code data} variable) from the message and update the
	 * views with information from that bundle.
	 * 
	 * @see #updateViews(boolean)
	 * @see General#log(String)
	 * @see General#logException(Exception)
	 * TODO
	 * */
	@SuppressLint("HandlerLeak")
	private void initializeThisMessenger()
	{
		if(thisMessenger != null)
		{ 
			return;
		}
		thisMessenger = new Messenger(new Handler()
		{
			@Override
			public void handleMessage(Message msg)
			{
				data = msg.getData();
				switch(msg.what)
				{
					case UserInfoPullService.GET_USER_INFO:
						updateViews(false);
						break;
					case UserInfoPullService.GET_USER_IMAGE:
						try
						{
							if(data.getByteArray(UserInfoPullService.BUNDLE_IMAGE) != null)
							{
								ByteArrayInputStream bais = new ByteArrayInputStream(msg.getData().getByteArray(UserInfoPullService.BUNDLE_IMAGE));
								image_drawable = Drawable.createFromStream(bais, getString(R.string.user_image_description));
								paintImage();								
							}
						}
						catch(Exception e)
						{
							app.log(ID + ".initializeThisMessenger.handleMessage(): Exception thrown.");
							app.logException(e);
						}
						break;
				}
			}
		});
	}
	
	/**
	 * Reference all the views used by this activity so they can be easily accessed.
	 * */
	private void referenceViews()
	{
		user_name = (TextView) findViewById(R.id.user_name_tv);
		status_number = (TextView) findViewById(R.id.user_status_number_tv);
		subscribers_number = (TextView) findViewById(R.id.user_subscribers_number_tv);
		subscriptions_number = (TextView) findViewById(R.id.user_subscriptions_number_tv);
		image = (ImageView) findViewById(R.id.user_image_iv);
	}
	
	/**
	 * Updates the views with the information stored in the bundle variable named {@code data}.
	 * 
	 * If the parameter is set to {@code true}, the views are only updated with the "refreshing"
	 * word.
	 * Otherwise they'll be updated with the information stored in the bundle {@code data}.
	 * 
	 * @param toRefreshing Is the boolean value indicating whether the views should display 
	 * 		"refreshing" or the information from the bundle {@code data}.
	 * */
	private void updateViews(boolean toRefreshing)
	{
		String name, status, subscribers, subscriptions;
		if(toRefreshing)
		{
			name = status = subscribers = subscriptions = getString(R.string.refreshing);
		}
		else
		{
			name = General.SPACE_STRING + data.getString(UserInfoPullService.BUNDLE_USER_NAME);
			status = General.SPACE_STRING + data.getString(UserInfoPullService.BUNDLE_STATUS_NUMBER);
			subscribers = General.SPACE_STRING + data.getString(UserInfoPullService.BUNDLE_SUBSCRIBERS_NUMBER);
			subscriptions = General.SPACE_STRING + data.getString(UserInfoPullService.BUNDLE_SUBSCRIPTIONS_NUMBER);
			paintImage();
		}
		user_name.setText(name);
		status_number.setText(status);
		subscribers_number.setText(subscribers);
		subscriptions_number.setText(subscriptions);
	}

	/**
	 * Draws an image from the {@code image_drawable} variable on the image view.
	 * */
	private void paintImage()
	{
		if(image_drawable == null)
		{
			image.setVisibility(ImageView.INVISIBLE);
			return;
		}
		image.setImageDrawable(image_drawable);
		image.setVisibility(ImageView.VISIBLE);
		image_drawable = null;
	}
	
	
//Activity methods
	
	/**
	 * Store the references from the views and bind to the service.
	 * 
	 * Store all the references of the views used by this activity, initializes the messenger that
	 * will receive the response from the service and make a request to bind with the service.
	 * 
	 * @see #referenceViews()
	 * @see #initializeThisMessenger()
	 * */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		app = (General) getApplication();
		setContentView(R.layout.activity_userinfo);
		referenceViews();
		initializeThisMessenger();
		serviceIntent = new Intent(this, UserInfoPullService.class);
		bindService(serviceIntent, this, Service.BIND_AUTO_CREATE);
	}

	/**
	 * Unbinds with the service once this activity is destroyed.
	 * 
	 * If the intent is different than null, means this client is probably binded with the service.
	 * If so, unbind with him and set the intent reference to null (help GC).
	 * */
	@Override
	public void onDestroy() 
	{
		if(serviceIntent != null)
		{
			unbindService(this);
			serviceIntent = null;
		}
		super.onDestroy();
	}
	    
	/**
	 * Updates the views with "refreshing" state, waiting for the information from the service
	 * and perform the request to the service UserInfoPullService.
	 *
	 * @see #updateViews(boolean)
	 * @see #getUserInfo()
	 * @see #getUserImage()
	 * */
	@Override
	public void onResume() 
	{
		super.onResume();
		updateViews(true);
		if(serviceMessenger != null)
		{
			getUserInfo();
			getUserImage();
		}
	}
	
	/**
   	 * Sets some menu items invisible (the ones that don't matter for this Activity).
   	 * 
   	 * Hide the refresh and user info menu items because there is none sense having them here.
   	 * */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) 
	{
		getMenuInflater().inflate(R.menu.shared_menu, menu);
		menu.getItem(3).setVisible(false);
		menu.getItem(4).setVisible(false);
		return true;
	}
	
}
